#include <jni.h>
#include <string>
#include <assert.h>
#include "FFmpegAudio.h"
#include "FFmpegVedio.h"
#include "FFmpegMusic.h"
//在cpp中引入c
extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include <android/native_window_jni.h>
#include <unistd.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "Log.h"
}

//#define TAG "zds-ffmpeg"
//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
//#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"itsen",FORMAT,##__VA_ARGS__);

//**************************回掉Yuv*******************************//
extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_mp42yuv_YuvActivity_result(JNIEnv *env, jobject jobj, jstring result_) {
    const char *result = env->GetStringUTFChars(result_, 0);
    //jclass Cpp
    jclass jclz = env->GetObjectClass(jobj);
    //mid
    jmethodID mid = env->GetMethodID(jclz, "outputRest", "([Ljava/lang/String;)V");
    //调用方法
    env->CallCharMethod(jclz, mid, result);

    env->ReleaseStringUTFChars(result_, result);
}


//**************************视频转YUV*******************************//
extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_mp42yuv_YuvActivity_open(JNIEnv *env, jobject instance, jstring inputStr_,
                                              jstring outStr_) {
    const char *inputStr = env->GetStringUTFChars(inputStr_, 0);
    const char *outStr = env->GetStringUTFChars(outStr_, 0);

    //视频流索引
    int vedio_stream_idx = -1;
    //文件抽象AVFormatContext
    AVFormatContext *pContext;
    //解码器上下文
    AVCodecContext *pCodecCtx;
    //解码器
    AVCodec *pCodex;
    //数据包
    AVPacket *packet;
    //格式转换上下文
    SwsContext *swsContext;

    // 1、注册各大组件
    av_register_all();

    //2、初始化一个AVFormatContext
     pContext = avformat_alloc_context();
    if (pContext == NULL) {
        LOGE("获取AVFormatContext失败");
        return;
    }

    //3、打开视频文件，并获取视频信息，选择解码器 {AVFormatContext与文件关联}
    if (avformat_open_input(&pContext, inputStr, NULL, NULL) != 0) {
        LOGE("打开文件失败");//文件打开失败注意动态权限问题
        return;
    }

    //4、视频流信息
    if (avformat_find_stream_info(pContext, NULL) < 0) {
        LOGE("获取信息失败");
        return;
    }

    //找到视频流
    for (int i = 0; i < pContext->nb_streams; ++i) {
        LOGE("循环  %d", i);
        //codec 每一个流 对应的解码上下文   codec_type 流的类型
        if (pContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            vedio_stream_idx = i;
        }
    }

    //获取-> 视频流的解码器上下文
    pCodecCtx = pContext->streams[vedio_stream_idx]->codec;

    //获取-> 解码器
    pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodex == NULL) {
        LOGE("获取解码器失败");
        return;
    }

    //打开解码器
    if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
        LOGE("解码失败");
        return;
    }

    //分配内存AVPacket 结构体内含缓存区
    packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    //初始化结构体
    av_init_packet(packet);

    AVFrame *frame;
    AVFrame *yuvFrame;
    uint8_t *out_buffer;

    frame = av_frame_alloc();
    yuvFrame = av_frame_alloc();
    //给yuvframe  的缓冲区 初始化
    out_buffer = (uint8_t *) av_malloc(
            avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));

    int re = avpicture_fill((AVPicture *) yuvFrame, out_buffer, AV_PIX_FMT_YUV420P,
                            pCodecCtx->width, pCodecCtx->height);

    LOGE("宽 %d  高 %d", pCodecCtx->width, pCodecCtx->height);

    // 格式转换上下文pCodecCtx->pix_fmt
    swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                            pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
                                            SWS_BILINEAR, NULL, NULL, NULL
    );

    int frameCount = 0;
    FILE *fp_yuv = fopen(outStr, "wb");

    int got_frame;
    while (av_read_frame(pContext, packet) >= 0) {//读取一帧压缩数据 循环直到全部读完
        //  解封装
        avcodec_decode_video2(pCodecCtx, frame, &got_frame, packet);
        // Java_com_itsen_model_YuvActivity_result();
        LOGE("解码%d  ", frameCount++);
        if (got_frame > 0) {
            sws_scale(swsContext, (const uint8_t *const *) frame->data, frame->linesize, 0,
                      frame->height, yuvFrame->data,
                      yuvFrame->linesize
            );
            int y_size = pCodecCtx->width * pCodecCtx->height;

            fwrite(yuvFrame->data[0], 1, y_size, fp_yuv);//Y
            fwrite(yuvFrame->data[1], 1, y_size / 4, fp_yuv);//U
            fwrite(yuvFrame->data[2], 1, y_size / 4, fp_yuv);//V
        }
        av_free_packet(packet);
    }

    //关闭和释放相关变量
    fclose(fp_yuv);
    av_frame_free(&frame);
    av_frame_free(&yuvFrame);
    avcodec_close(pCodecCtx);
    avformat_free_context(pContext);
    //乱码问题
    env->ReleaseStringUTFChars(inputStr_, inputStr);
    env->ReleaseStringUTFChars(outStr_, outStr);
}

//********************************** 视频播放 player start *****************************************
extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_player_VideoView_render(JNIEnv *env, jobject instance, jstring input_,
                                             jobject surface) {
    const char *input = env->GetStringUTFChars(input_, 0);
    av_register_all();

    AVFormatContext *pFormatCtx = avformat_alloc_context();
    //第四个参数是 可以传一个 字典   是一个入参出参对象
    if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
        LOGE("%s", "打开输入视频文件失败");
    }
    //获取视频信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("%s", "获取视频信息失败");
        return;
    }

    int vidio_stream_idx = -1;
    int i = 0;
    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            LOGE("  找到视频id %d", pFormatCtx->streams[i]->codec->codec_type);
            vidio_stream_idx = i;
            break;
        }
    }

    //获取视频编解码器
    AVCodecContext *pCodecCtx = pFormatCtx->streams[vidio_stream_idx]->codec;
    LOGE("获取视频编码器上下文 %p  ", pCodecCtx);
    AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
    LOGE("获取视频编码 %p", pCodex);

    //打开解器
    if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
        LOGE("解码失败");
        return;
    }

    //包数据、帧数据
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    AVFrame *frame;
    frame = av_frame_alloc();

    //RGB格式的帧数据
    AVFrame *rgb_frame = av_frame_alloc();
    //    给缓冲区分配内存
    //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
    //缓冲区分配内存
    uint8_t *out_buffer = (uint8_t *) av_malloc(
            avpicture_get_size(AV_PIX_FMT_RGBA, pCodecCtx->width, pCodecCtx->height));
    LOGE("宽  %d,  高  %d  ", pCodecCtx->width, pCodecCtx->height);
    //设置yuvFrame的缓冲区，像素格式
    int re = avpicture_fill((AVPicture *) rgb_frame, out_buffer, AV_PIX_FMT_RGBA, pCodecCtx->width,
                            pCodecCtx->height);
    LOGE("申请内存%d   ", re);

    //    输出需要改变
    int length = 0;
    int got_frame;
    //    输出文件
    int frameCount = 0;
    SwsContext *swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                            pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGBA,
                                            SWS_BICUBIC, NULL, NULL, NULL
    );
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
    //    视频缓冲区
    ANativeWindow_Buffer outBuffer;
    //    ANativeWindow
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        //        AvFrame
        if (packet->stream_index == vidio_stream_idx) {
            length = avcodec_decode_video2(pCodecCtx, frame, &got_frame, packet);
            LOGE(" 获得长度   %d ", length);

            //非零   正在解码
            if (got_frame) {
                // 绘制之前   配置一些信息  比如宽高   格式
                ANativeWindow_setBuffersGeometry(nativeWindow, pCodecCtx->width, pCodecCtx->height,
                                                 WINDOW_FORMAT_RGBA_8888);
                // 绘制
                ANativeWindow_lock(nativeWindow, &outBuffer, NULL);
                //h 264   ----yuv          RGBA
                LOGE("解码%d帧", frameCount++);
                //转为指定的YUV420P
                sws_scale(swsContext, (const uint8_t *const *) frame->data, frame->linesize, 0,
                          pCodecCtx->height, rgb_frame->data,
                          rgb_frame->linesize);
                //rgb_frame是有画面数据
                uint8_t *dst = (uint8_t *) outBuffer.bits;
                //拿到一行有多少个字节 RGBA
                int destStride = outBuffer.stride * 4;
                //像素数据的首地址
                uint8_t *src = (uint8_t *) rgb_frame->data[0];
                //实际内存一行数量
                int srcStride = rgb_frame->linesize[0];
                int i = 0;
                for (int i = 0; i < pCodecCtx->height; ++i) {
                    //memcpy(void *dest, const void *src, size_t n)
                    memcpy(dst + i * destStride, src + i * srcStride, srcStride);
                }
                ANativeWindow_unlockAndPost(nativeWindow);
                usleep(1000 * 16);
            }
        }
        av_free_packet(packet);
    }
    ANativeWindow_release(nativeWindow);
    av_frame_free(&frame);
    avcodec_close(pCodecCtx);
    avformat_free_context(pFormatCtx);
    env->ReleaseStringUTFChars(input_, input);
}

//****************************** 音频转PCM *********************************************
extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_mp32pcm_PcmActivity_sound(JNIEnv *env, jobject instance, jstring input_,
                                               jstring outut_) {
    const char *input = env->GetStringUTFChars(input_, 0);
    const char *outut = env->GetStringUTFChars(outut_, 0);

    //1、注册
    av_register_all();

    //2、初始化一个AVFormatContext容器
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    //3、打开文件 ：AVFormatContext 与文件关联
    if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
        LOGE("%s", "打开输入音频文件失败");
    }
    //4.获取文件信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("%s", "获取音频信息失败");
        return;
    }

    //5、音频流索引
    int audio_stream_idx = -1;
    int i = 0;
    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            LOGE("  找到音频id %d", pFormatCtx->streams[i]->codec->codec_type);
            audio_stream_idx = i;
            break;
        }
    }

    if (audio_stream_idx == -1) {
        LOGE("未找到音频流");
        return;
    }

    //6、获取音频编解码器
    AVCodecContext *pCodecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
    AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);

    //打开解码器 ：小于0就是打败失败了
    if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
        LOGE("解码失败");
        return;
    }

    //音频数据
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    AVFrame *frame;
    frame = av_frame_alloc();

    //格式转换容器 SwcContext
    SwrContext *swrContext = swr_alloc();

    int length = 0;
    int got_frame;


    //44100*2 缓存
    uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);

    //输出声道数
    uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    //量化格式
    enum AVSampleFormat out_formart = AV_SAMPLE_FMT_S16;
    //输出的采样率必须与输入相同
    int out_sample_rate = pCodecCtx->sample_rate;

    /*音频的数据化描述 三要素
     * 量化格式 16比特
     * 采样率   44100次
     * 声道数   2
     */

    /* @param s               existing Swr context if available, or NULL if not
    * @param out_ch_layout   output channel layout (AV_CH_LAYOUT_*)
    * @param out_sample_fmt  output sample format (AV_SAMPLE_FMT_*).
    * @param out_sample_rate output sample rate (frequency in Hz)
    * @param in_ch_layout    input channel layout (AV_CH_LAYOUT_*)
    * @param in_sample_fmt   input sample format (AV_SAMPLE_FMT_*).
    * @param in_sample_rate  input sample rate (frequency in Hz)
    * @param log_offset      logging level offset
    * @param log_ctx         parent logging context, can be NULL
    */
    swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,
                       pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,
                       NULL);

    swr_init(swrContext);
    //  获取通道数  2
    int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

    //    输出文件
    FILE *pcm_file = fopen(outut, "wb");
    int frameCount = 0;
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audio_stream_idx) {
            //解码  mp3   编码格式frame----pcm   frame
            avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);
            if (got_frame) {
                LOGE("解码");
                /**
                 * int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                const uint8_t **in , int in_count);
                 */
                swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data,
                            frame->nb_samples);
                // 缓冲区的大小
                int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,
                                                      AV_SAMPLE_FMT_S16, 1);
                fwrite(out_buffer, 1, size, pcm_file);
            }
        }
    }
    fclose(pcm_file);
    av_frame_free(&frame);
    swr_free(&swrContext);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    env->ReleaseStringUTFChars(input_, input);
    env->ReleaseStringUTFChars(outut_, outut);
}

//********************************* 音频播放 plear2 ******************************************

extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_player2_Player_sound(JNIEnv *env, jobject instance, jstring input_,
                                          jstring output_) {
    const char *input = env->GetStringUTFChars(input_, 0);
    const char *output = env->GetStringUTFChars(output_, 0);

    av_register_all();

    AVFormatContext *pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
        LOGE("%s", "打开输入视频文件失败");
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("%s", "获取视频信息失败");
        return;
    }

    //音频流索引
    int audio_stream_idx = -1;
    int i = 0;
    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            LOGE("  找到音频id %d", pFormatCtx->streams[i]->codec->codec_type);
            audio_stream_idx = i;
            break;
        }
    }

    //获取音频编解码器
    AVCodecContext *pCodecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
    LOGE("获取视频编码器上下文 %p  ", pCodecCtx);

    AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
    LOGE("获取视频编码 %p", pCodex);

    if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
    }
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));

    //音频数据
    AVFrame *frame;
    frame = av_frame_alloc();

    //mp3  里面所包含的编码格式   转换成  pcm   SwcContext
    SwrContext *swrContext = swr_alloc();

    int length = 0;
    int got_frame;

    //44100*2
    uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);

    uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    //输出采样位数  16位
    enum AVSampleFormat out_formart = AV_SAMPLE_FMT_S16;
    //输出的采样率必须与输入相同
    int out_sample_rate = pCodecCtx->sample_rate;


    swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,
                       pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,
                       NULL);

    swr_init(swrContext);

    // 获取通道数  2
    int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
    //    反射得到Class类型
    jclass david_player = env->GetObjectClass(instance);
    //    反射得到createAudio方法
    jmethodID createAudio = env->GetMethodID(david_player, "createAudio", "(II)V");
    //    反射调用createAudio
    env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb);
    jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");

    int frameCount = 0;
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audio_stream_idx) {
            // 解码  mp3   编码格式frame----pcm   frame
            avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);
            if (got_frame) {
                LOGE("解码");
                swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data,
                            frame->nb_samples);
                //缓冲区的大小
                int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,
                                                      AV_SAMPLE_FMT_S16, 1);
                jbyteArray audio_sample_array = env->NewByteArray(size);
                env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer);
                //反射调用playTrack
                env->CallVoidMethod(instance, audio_write, audio_sample_array, size);
                env->DeleteLocalRef(audio_sample_array);
            }
        }
    }
    av_frame_free(&frame);
    swr_free(&swrContext);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    env->ReleaseStringUTFChars(input_, input);
    env->ReleaseStringUTFChars(output_, output);
}
//****************************** opensl es 播放音频demo *********************************************

//--引擎
//引擎接口对象
SLObjectItf engineObject = NULL;
//具体的引擎对象实例
SLEngineItf engineEngine;

//--混音器
//混音器接口对象
SLObjectItf outputMixObject = NULL;
//具体的混音器对象实例
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;

//--播放器
//播放器接口对象
SLObjectItf bqPlayerObject = NULL;
//具体的播放器对象实例
SLPlayItf bqPlayerPlay;

SLVolumeItf bqPlayerVolume;
const SLEnvironmentalReverbSettings settings = SL_I3DL2_ENVIRONMENT_PRESET_DEFAULT;
size_t bufferSize;
void *buffer;

//缓冲器队列接口
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;

// 当喇叭播放完声音时回调此方法
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
    bufferSize = 0;
    //assert(NULL == context);
    getPCM(&buffer, &bufferSize);
    // for streaming playback, replace this test by logic to find and fill the next buffer
    if (NULL != buffer && 0 != bufferSize) {
        SLresult result;
        // enqueue another buffer
        result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer,
                                                 bufferSize);
        // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
        // which for this code example would indicate a programming error
        assert(SL_RESULT_SUCCESS == result);
        LOGE("david  bqPlayerCallback :%d", result);
    }
}


extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_opensl_Player_openslplayer(JNIEnv *env, jobject instance) {

    //返回结果
    SLresult sLresult;
    //第 1 步创建引擎
    slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    //实现（Realize）engineObject接口对象
    (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    //通过engineObject的GetInterface方法初始化engineEngine
    (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    LOGE("地址 %p", engineEngine);
    //用音频引擎调用函数 创建混音器outputMixObject
    (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
    //实现混音器outputMixObject
    (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    //获取混音器接口outputMixEnvironmentalReverb
    (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                     &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == sLresult) {
        (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &settings);
    }
    int rate;
    int channels;
    createFFmpeg(&rate, &channels);
    LOGE(" 比特率%d  ,channels %d", rate, channels);
    //配置信息设置
    SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                            2};
    /*
     *  PCM 格式配置
     *
     * SLuint32 		formatType;
	 *  SLuint32 		numChannels;
	 *  SLuint32 		samplesPerSec;
	 *   SLuint32 		bitsPerSample;
	 *   SLuint32 		containerSize;
	 *   SLuint32 		channelMask;
	  *  SLuint32		endianness;
     */
    SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1, SL_PCMSAMPLEFORMAT_FIXED_16,
                            SL_PCMSAMPLEFORMAT_FIXED_16,
                            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
                            SL_BYTEORDER_LITTLEENDIAN};
    //新建一个数据源 将上述配置信息放到这个数据源中
    SLDataSource slDataSource = {&android_queue, &pcm};
    //设置混音器
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};

    SLDataSink audioSnk = {&outputMix, NULL};
    //创建Recorder需要 RECORD_AUDIO 权限
    //SLInterfaceID slInterfaceID[2]={SL_IID_ANDROIDSIMPLEBUFFERQUEUE,SL_IID_ANDROIDCONFIGURATION};
    //SLboolean reqs[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
    // create audio player
    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND,
            /*SL_IID_MUTESOLO,*/ SL_IID_VOLUME};
    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
            /*SL_BOOLEAN_TRUE,*/ SL_BOOLEAN_TRUE};
    int reslut = SL_RESULT_SUCCESS == sLresult;
    //先讲这个
    sLresult = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &slDataSource,
                                                  &audioSnk, 3,
                                                  ids, req);
    LOGE("初始化播放器%d  是否成功 %d   bqPlayerObject  %d", sLresult, reslut, bqPlayerObject);
    //初始化播放器
    (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);

    //得到接口后调用  获取Player接口
    (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);

    //注册回调缓冲区 //获取缓冲队列接口
    (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
                                    &bqPlayerBufferQueue);
    LOGE("获取缓冲区数据");
    //缓冲接口回调

    (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
    //获取音量接口
    (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);

    //设置播放状态
    (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);

    //主动调用回调函数开始工作
    bqPlayerCallback(bqPlayerBufferQueue, NULL);
}

// shut down the native audio system
void shutdown() {
    // destroy buffer queue audio player object, and invalidate all associated interfaces
    if (bqPlayerObject != NULL) {
        (*bqPlayerObject)->Destroy(bqPlayerObject);
        bqPlayerObject = NULL;
        bqPlayerPlay = NULL;
        bqPlayerBufferQueue = NULL;
        bqPlayerVolume = NULL;
    }
    // destroy output mix object, and invalidate all associated interfaces
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }
    // destroy engine object, and invalidate all associated interfaces
    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;
    }
    // 释放FFmpeg解码器相关资源
    realase();
}

extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_opensl_Player_openslpStop(JNIEnv *env, jobject instance) {
    shutdown();
}

//**************************** AV 同步 ***********************************************

ANativeWindow *window = 0;
const char *path;
FFmpegAudio *audio;
FFmpegVedio *vedio;
pthread_t p_tid;
int isPlay = 0;



extern "C"
void call_video_play(AVFrame *frame) {
    if (!window) {
        return;
    }
    ANativeWindow_Buffer window_buffer;
    if (ANativeWindow_lock(window, &window_buffer, 0)) {
        return;
    }

    LOGE("绘制 宽%d,高%d", frame->width, frame->height);
    LOGE("绘制 宽%d,高%d  行字节 %d ", window_buffer.width, window_buffer.height, frame->linesize[0]);
    uint8_t *dst = (uint8_t *) window_buffer.bits;
    int dstStride = window_buffer.stride * 4;
    uint8_t *src = frame->data[0];
    int srcStride = frame->linesize[0];
    for (int i = 0; i < window_buffer.height; ++i) {
        memcpy(dst + i * dstStride, src + i * srcStride, srcStride);
    }
    ANativeWindow_unlockAndPost(window);
}

//解码函数
extern "C"
void *process(void *args) {
    LOGE("开启解码线程");
    //1.注册组件
    av_register_all();
    avformat_network_init();
    //封装格式上下文
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    //2.打开输入视频文件
    if (avformat_open_input(&pFormatCtx, path, NULL, NULL) != 0) {
        LOGE("%s", "打开输入视频文件失败");
    }
    //3.获取视频信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("%s", "获取视频信息失败");
    }

    //视频解码，需要找到视频对应的AVStream所在pFormatCtx->streams的索引位置
    int i = 0;
    for (; i < pFormatCtx->nb_streams; i++) {
        //4.获取视频解码器
        AVCodecContext *pCodeCtx = pFormatCtx->streams[i]->codec;
        AVCodec *pCodec = avcodec_find_decoder(pCodeCtx->codec_id);

        AVCodecContext *codec = avcodec_alloc_context3(pCodec);
        avcodec_copy_context(codec, pCodeCtx);
        if (avcodec_open2(codec, pCodec, NULL) < 0) {
            LOGE("%s", "解码器无法打开");
            continue;
        }
        //根据类型判断，是否是视频流
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            /*找到视频流*/
            vedio->setAvCodecContext(codec);
            vedio->index = i;
            vedio->time_base = pFormatCtx->streams[i]->time_base;
            if (window)
                ANativeWindow_setBuffersGeometry(window, vedio->codec->width,
                                                 vedio->codec->height,
                                                 WINDOW_FORMAT_RGBA_8888);

        } else if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio->setAvCodecContext(codec);
            audio->index = i;
            audio->time_base = pFormatCtx->streams[i]->time_base;
        }

    }

    //开启 音频 视频  播放的死循环
    vedio->setAudio(audio);
    vedio->play();
    audio->play();
    isPlay = 1;
    //解码packet

    //编码数据
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    //解码完整个视频 子线程
    int ret;
    while (isPlay) {
        //如果这个packet  流索引 等于 视频流索引 添加到视频队列
        ret = av_read_frame(pFormatCtx, packet);
        if (ret == 0) {
            if (vedio && vedio->isPlay && packet->stream_index == vedio->index) {
                vedio->put(packet);
            } else if (audio && audio->isPlay && packet->stream_index == audio->index) {
                audio->put(packet);
            }
            av_packet_unref(packet);
        } else if (ret == AVERROR_EOF) {
            //读取完毕 但是不一定播放完毕
            while (isPlay) {
                if (vedio->queue.empty() && audio->queue.empty()) {
                    break;
                }
                //LOGI("等待播放完成");
                av_usleep(10000);
            }
        }
    }
    //视频解码完可能视频播放完了也可能视频没播放完成
    isPlay = 0;
    if (vedio && vedio->isPlay) {
        vedio->stop();
    }
    if (audio && audio->isPlay) {
        audio->stop();
    }
    av_free_packet(packet);
    avformat_free_context(pFormatCtx);
    pthread_exit(0);

}


extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_vaplayer_ItPlayer_display(JNIEnv *env, jobject instance, jobject surface) {

    if (window) {
        ANativeWindow_release(window);
        window = 0;
    }
    window = ANativeWindow_fromSurface(env, surface);
    if (vedio && vedio->codec) {
        ANativeWindow_setBuffersGeometry(window, vedio->codec->width, vedio->codec->height,
                                         WINDOW_FORMAT_RGBA_8888);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_vaplayer_ItPlayer_release(JNIEnv *env, jobject instance) {

    if (isPlay) {
        isPlay = 0;
        pthread_join(p_tid, 0);
    }
    if (vedio) {
        if (vedio->isPlay) {
            vedio->stop();
        }
        delete (vedio);
        vedio = 0;
    }
    if (audio) {
        if (audio->isPlay) {
            audio->stop();
        }
        delete (audio);
        audio = 0;
    }
}


extern "C"
JNIEXPORT void JNICALL
Java_com_itsen_model_vaplayer_ItPlayer_play(JNIEnv *env, jobject instance, jstring path_) {
    path = env->GetStringUTFChars(path_, 0);
    //实例化对象
    vedio = new FFmpegVedio;
    audio = new FFmpegAudio;
    vedio->setPlayCall(call_video_play);
    pthread_create(&p_tid, NULL, process, NULL);
    env->ReleaseStringUTFChars(path_, path);
}

//**************************************************************************


//**************************************************************************
